package ee.parameterConverter.impl;

import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

import ee.parameterConverter.ConversionFailedException;
import ee.parameterConverter.Converter;
import ee.parameterConverter.ConverterResolver;
import ee.parameterConverter.EmptyValueException;
import ee.parameterConverter.GenericStringConverter;
import ee.parameterConverter.TypeKind;
import ee.parameterConverter.TypeKindResolver;

public class ConverterResolverImpl implements ConverterResolver {

	private TypeKindResolver _typeKindResolver;
	
	private Map<Type, Converter<Map<String, String[]>, ?>> _parameterConverters;
	private Map<Type, Converter<String[], ?>> _stringArrayConverters;
	private Map<Type, Converter<String, ?>> _stringConverters;

	private GenericStringConverter _genericStringConverter;
	
	public ConverterResolverImpl(TypeKindResolver typeKindResolver, GenericStringConverter genericStringConverter)
	{
		_typeKindResolver = typeKindResolver;
		_genericStringConverter = genericStringConverter;
		
		_parameterConverters = new HashMap<Type, Converter<Map<String,String[]>,?>>();
		_stringArrayConverters = new HashMap<Type, Converter<String[], ?>>();
		_stringConverters = new HashMap<Type, Converter<String,?>>();
	}
	
	@Override
	public Converter<Map<String, String[]>, ?> getParameterConverter(Type type) {
		return _parameterConverters.get(type);
	}

	@Override
	public Converter<String[], ?> getStringArrayConverter(Type type) {
		
		if (_stringArrayConverters.containsKey(type))
		{
			return _stringArrayConverters.get(type);
		}
		
		//see if we can use a generic converter
		TypeKind typeKind = _typeKindResolver.resolve(type);
		
		Converter<String[], ?> converter;
		
		if (typeKind.equals(TypeKind.Simple))
		{
			converter = _genericStringConverter.getStringArrayConverter(type);
		} else
		{
			converter = null;
		}
		
		// check if we can use a string converter
		if (_stringConverters.containsKey(type))
		{
			converter = new StringConverterWrapper(_stringConverters.get(type), converter);
		}
		
		return converter;
	}

	@Override
	public Converter<String, ?> getStringConverter(Type type) {
		
		if (_stringConverters.containsKey(type))
		{
			return _stringConverters.get(type);
		}

		//see if we can use a generic converter
		TypeKind typeKind = _typeKindResolver.resolve(type);
		
		if (typeKind.equals(TypeKind.Simple))
		{
			return _genericStringConverter.getStringConverter(type);
		}
		
		return null;
	}

	public void addParameterConverter(Type type, Converter<Map<String, String[]>, ?> converter)
	{
		_parameterConverters.put(type, converter);
	}
	
	public void addStringArrayConverter(Type type, Converter<String[], ?> converter)
	{
		_stringArrayConverters.put(type, converter);
	}
	
	public void addStringConverter(Type type, Converter<String, ?> converter)
	{
		_stringConverters.put(type, converter);
	}
	
	static public class StringConverterWrapper implements Converter<String[], Object>
	{
		private Converter<String, ?> _stringConverter;
		private Converter<String[], ?> _genericConverter;
		
		public StringConverterWrapper(Converter<String, ?> stringConverter, Converter<String[], ?> genericConverter)
		{
			_stringConverter = stringConverter;
			_genericConverter = genericConverter;
		}
		
		@Override
		public Object convert(String[] value) throws EmptyValueException, ConversionFailedException {
			
			if (value != null && value.length == 1)
			{
				return _stringConverter.convert(value[0]);
			}
			
			if (_genericConverter == null)
			{
				throw new ConversionFailedException("Could not convert the value, no generic converter found", value);
			}
			
			return _genericConverter.convert(value);
		}
		
	}
}
